<template>
  <div class="validate-input-container pb-3">
    <input
      v-if="tag === 'input'"
      class="form-control input"
      :class="{ 'is-invalid': inputRef.error }"
      @blur="validateInput"
      v-model="inputRef.val"
      v-bind="$attrs"
    />
    <!-- @input="updateValue" :value="inputRef.val"-->
    <textarea
      v-else
      class="form-control input"
      :class="{ 'is-invalid': inputRef.error }"
      v-model="inputRef.val"
      @blur="validateInput"
      v-bind="$attrs"
    />
    <!-- 要手动添加v-model必须要把v-model="inputRef.val"改成 
            :value="inputRef.val" 
            @input="updateValue"-->
    <span v-if="inputRef.error" class="invalid-feedback error">{{
      inputRef.message
    }}</span>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, PropType, onMounted, computed } from "vue";
// 1. 把父组件中已经建立好的监听器导入进来
import { emitter } from "./ValidateForm.vue";

// 表单中的邮箱验证规则
const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
// 密码框的验证规则 密码至少包含六位 不能含有空格
// 非空白字符 \S {6,}
// const rangeReg = /^(?![^a-zA-Z]+$)(?!\D+$).{8,15}$/
const rangeReg = /^\S*$/;

// interface用于定义规则RuleProp
interface RuleProp {
  // 定义规则的类型 这里是可以扩展的
  type: "required" | "email" | "range" | "custom";
  message?: string;
  min?: { message: string; length: number };
  max?: { message: string; length: number };
  validator?: () => boolean;
}

// 定义想传入的对象
type TagType = "textarea" | "input";

// 定义类型RulesProp是RuleProp的数组 App组件传入的是数组形式的
export type RulesProp = RuleProp[];

export default defineComponent({
  props: {
    // 定义传来的rules数组 断言成RulesProp类型
    rules: Array as PropType<RulesProp>,
    // 支持v-model 自定义的组件需要添加这个modelValue
    modelValue: String,
    // 传入的tag 定义是textarea 还是input
    tag: {
      type: String as PropType<TagType>,
      default: "input"
    }
  },
  inheritAttrs: false, //传入的非prop的属性不希望添加到组件的根节点上
  setup(props, context) {
    //console.log(context.attrs);// Proxy {placeholder: "hello !", __vInternal: 1, onUpdate:modelValue: ƒ}

    // 表单元素的响应式数据 inputRef
    const inputRef = reactive({
      val: computed({
        get: () => props.modelValue || "", //假如传了v-model就会放到 modelValue
        set: val => {
          context.emit("update:modelValue", val);
        }
      }),
      error: false,
      message: ""
    });
    /* 以下的写法可以被上面的computed所代替！ 并且可以避免触发两次重复更新-watch获取更新操作
    watch(
      () => props.modelValue,
      newValue => {
        inputRef.val = newValue || "";
      }
    );
    // input事件触发的回调函数
    const updateValue = (e: KeyboardEvent) => {
      // 要断言成HTMLInputElement，拿到触发键盘事件的value值
      const targetValue = (e.target as HTMLInputElement).value;
      inputRef.val = targetValue; //把reactive响应式数据改成targetValue
      context.emit("update:modelValue", targetValue); // 自定义的事件 参数为targetValue
    };*/

    // 验证通过与否的回调函数
    const validateInput = () => {
      // 是否存在rules规则
      if (props.rules) {
        // 使用every 当其中每一项都为true的时候才为true
        const allPassed = props.rules.every(rule => {
          // 定义一个passed临时变量看是否通过所有的条件 如果false立刻跳出循环
          let passed = true;
          // 把读取到的message赋值给inputRef响应式对象
          if (rule.message) {
            inputRef.message = rule.message;
          }

          // switch语句是有先后顺序的
          switch (rule.type) {
            // 1,判断是否是required条件 如果不满足这个条件passed改为false 就会立刻跳出every循环
            case "required":
              passed = inputRef.val.trim() !== "";
              break;
            // 2,判断是否是email条件 如果满足这个条件passed还是true
            case "email":
              passed = emailReg.test(inputRef.val);
              break;
            // 3. 判断是否满足range条件
            case "range":
              if (
                rule.min &&
                (rule.min.length > inputRef.val.length ||
                  !rangeReg.test(inputRef.val))
              ) {
                passed = false;
                inputRef.message = rule.min.message;
              }
              if (
                passed &&
                rule.max &&
                (rule.max.length < inputRef.val.length ||
                  !rangeReg.test(inputRef.val))
              ) {
                passed = false;
                inputRef.message = rule.max.message;
              }
              break;
            case "custom":
              passed = rule.validator ? rule.validator() : true;
              break;
            default:
              break;
          }
          return passed;
        });
        // 把allPassed结果取反给error
        inputRef.error = !allPassed;
        // console.log("allpassed+", allPassed);
        return allPassed;
        // 可以返回更多，例如错误的信息等等
      } else {
        return true;
      }
    };

    // 清空表单的回调函数 还要将input的状态设置成初始状态
    const clearInput = () => {
      if (inputRef.val) {
        inputRef.val = "";
        inputRef.error = false;
      }
    };

    // 发送事件
    onMounted(() => {
      // 发送出事件form-item-created，会将验证函数发送出去
      // validateInput函数其格式对应着 type ValidateFunc = () => boolean;
      emitter.emit("form-item-created", validateInput);
      emitter.emit("form-item-clear", clearInput);
    });

    return {
      inputRef,
      validateInput
    };
  }
});
</script>

<style lang="css">
.input {
  position: relative;
}
.error {
  position: absolute;
}
</style>
